#include <stdio.h>
#include <stdlib.h>
#include <string.h>


//qsort  默认升序
//void qsort(void* base ,   //base中存放的是待排数组的第一个元素地址
//           size_t num,    //num是base指向数组的元素个数
//           size_t size,   //size是base指向数组中一个元素的长度,单位:字节
//           int(*compar)(const void* e1,const void* e2)  //函数指针_指向一个比较函数,这个比较函数用来比较数组中两个元素的大小
//             //如果e1指向元素大于e2指向元素,那么函数返回一个大于0的数
//            //如果e1指向元素等于e2指向元素,那么函数返回0
//            //如果e1指向元素小于e2指向元素,那么函数返回一个小于0的数
//           );

typedef struct {
    char name[20];
    int age;
}Stu;

int compar_int(const void* e1,const void* e2){              //void*指针不能解引用操作,也不能+/-操作
    return *(int*)e1 - *(int*)e2;
}
//使用 -> 运算符访问结构体成员时确实涉及到解引用，但这一过程是隐式的。当你拥有一个指向结构体的指针时，使用 -> 运算符可以直接访问该结构体的成员，而无需显式地使用解引用操作符 *。这使得代码更简洁易读。
//例如，如果你有一个指向 Stu 类型结构体的指针 stu_ptr，你可以直接使用 stu_ptr->age 来访问 age 成员，而不需要先解引用指针再使用点运算符（(*stu_ptr).age）。这是因为 -> 运算符自动处理了指针的解引用。
int compar_age(const void* e1,const void* e2){              //void*指针不能解引用操作,也不能+/-操作

    return ((const Stu*)e1)->age - ((const Stu*)e2)->age;  //-> 运算符的优先级高于类型转换。
}
//按照名字比较大小用strcmp
//str(字符1,字符2)
//如果字符串1大于字符串2返回一个大于0的数
//如果字符串1大于字符串2返回一个0
//如果字符串1小于字符串2返回一个小于0的数
int compar_name(const void* e1,const void* e2){              //void*指针不能解引用操作,也不能+/-操作

    return strcmp(((const Stu*)e1)->name , ((const Stu*)e2)->name);  //-> 运算符的优先级高于类型转换。
}

//冒泡排序改造
void Swap(char* buff_1,char* buff_2,size_t width){
    for(int i = 0;i < width;i++){
        char temp = *buff_1;
        *buff_1 = *buff_2;
        *buff_2 = temp;
        buff_1++;
        buff_2++;
    }
}
void bubble_sort(void* arr,size_t len,size_t width,int (*cmp)(const void* e1 ,const void* e2)){
    for(int i = 0;i < len - 1;i++){
        for(int j = 0;j < len-1-i;j++){
            if(cmp((char*)(arr+j*width),(char*)(arr+(j+1)*width)) > 0){
                Swap((char*)(arr+j*width),(char*)(arr+(j+1)*width),width);
            }

        }
    }
}


void print_int (int* arr,int len){
    for(int i = 0;i < len;i++){
        printf("%d ",arr[i]);
    }
}
void print_Stu (Stu* arr,int len){   //!!arr为一个结构体数组时则arr[i]为数组元素,而不是结构体成员地址(arr[i].成员),即不能用arr[i]->成员(arr[i]不是地址)
    for(int i = 0;i < len;i++){
        Stu* s = &arr[i];
        printf("Student %d: Name = %s, Age = %d\n",i+1,s->name,s->age);
    }
}
void test_1(){
    int arr[10] = {2,3,2,1,4,5,5,4,4,2};
    size_t len = sizeof (arr)/sizeof (arr[0]);
    qsort(arr,len,sizeof (arr[0]),compar_int);
    print_int(arr,len);
}




void test_2(){
    //!!聚合初始化只能在数组声明时使用
    Stu tu_arr[3] = {{"zhangshan",19},{"lisi",17},{"wangwu",47}};
    int len = sizeof (tu_arr)/sizeof (tu_arr[0]);
    qsort(tu_arr,len,sizeof (tu_arr[0]),compar_age);
    print_Stu(tu_arr,len);
}
void test_3(){
    //聚合初始化只能在数组声明时使用
    Stu tu_arr[3] = {{"zhangshan",19},{"lisi",17},{"wangwu",7}};
    int len = sizeof (tu_arr)/sizeof (tu_arr[0]);
    qsort(tu_arr,len,sizeof (tu_arr[0]),compar_name);
    print_Stu(tu_arr,len);
}
//测试void bubble_sort(void* arr,size_t len,size_t width,int (*cmp)(const void* e1 ,const void* e2))

void test_4(){
    int arr[10] = {2,3,7,1,6,8,5,9,0,4};
    size_t len = sizeof (arr)/sizeof (arr[0]);
    bubble_sort(arr,len,sizeof (arr[0]),compar_int);
    print_int(arr,len);
}
void test_5(){
    //聚合初始化只能在数组声明时使用
    Stu tu_arr[3] = {{"zhangshan",19},{"lisi",17},{"wangwu",47}};
    int len = sizeof (tu_arr)/sizeof (tu_arr[0]);
    bubble_sort(tu_arr,len,sizeof (tu_arr[0]),compar_age);
    print_Stu(tu_arr,len);
}
//结构体成员访问
//结构体变量.成员名
//结构体指针->成员名

//!!!结构体传参,尽量使用地址传递
//int main()
//{
////    test_1();
////    test_2();
////    test_3();
////    test_4();
////    test_5();
//
//    return 0;
//}

//练习使用库函数，qsort排序各种类型的数据
void Print(int* arr,int len){
    for(int i = 0;i < len ;i++){
        printf("%d ",arr[i]);
    }

}

//整数排序
int comp_int(const void* e1,const void* e2){
    return *((int*)e1) - *((int*)e2);
}
void test1(){
    int arr[10] = {3,4,5,62,8,231,53,2,4,1};
    size_t len = sizeof (arr)/sizeof (arr[0]);
    qsort(arr,len,sizeof arr[0],comp_int);
    Print(arr,len);

}

//字符串排序
int comp_str(const void* e1,const void* e2){
    const char* pe1 = *(const char**)e1;
    const char* pe2 = *(const char**)e2;
    return strcmp(pe1,pe2);               //!!因为这些void*指针指向的是char*指针，不是char值。strcmp(*((char*)e1),*((char*)e2));是不合法的
//比较字符    char1 - char2，这样可以直接比较两个字符的 ASCII 值。
}
void test2(){
    char str_1[] = "huwagh";
    char str_2[] = "china";
    char* str_sum[] = {str_1,str_2};
    size_t len = sizeof (str_sum)/sizeof (str_sum[0]);
    qsort(str_sum,len,sizeof (str_sum[0]), comp_str);
    for(int i = 0;i < len; i++){
        printf("%s ",str_sum[i]);
//        使用 %s 时：传入字符串（char*）的指针。
//        使用 %c 时：传入字符（char）本身。
//        使用 %d、%i 时：传入整数。
//        使用 %f 时：传入浮点数。
    }
}

//结构体排序
typedef struct {
    char name[20];
    int age;
}tu;
int comp_jgt(const void* e1,const void* e2){
    return ((const tu*)e1)->age - ((const tu*)e2)->age;
}
void test3(){
    tu tu_1[3] = {{"ahd",18},{"ewrarq",4},{"jiqar",11}};
    int len = sizeof (tu_1)/sizeof (tu_1[0]);
    qsort(tu_1,len,sizeof (tu_1[0]),comp_jgt);
    print_Stu(tu_1,len);
};


//int main(){
////    test1();
////test2();
//    test3();
//    return 0;
//}


//冒泡排序模拟qsort
void exchange(char* e1,char* e2,size_t size){
    for(int i = 0;i < size;i++ ){
        char temp = *e1;               //临时变量存放初始地址放的值,而不是保存地址
        *e1 = *e2;
        *e2 = temp;
        e1++;
        e2++;
    }
}
int compare(const void* e1,const void* e2){
    return *(int*)e1 - *(int*)e2;
}
void bubble_sort_new(void* arr,size_t len,size_t size,int(*compare)(const void* e1,const void* e2)){
    for(int i = 0;i < len -1;i++){
        for(int j = 0;j < len - 1 - i;j++){
            if(compare((char*)(arr+j*size),(char*)(arr+(1+j)*size)) > 0){
                exchange((char*)(arr+j*size),(char*)(arr+(1+j)*size),size);
            }
        }
    }
}
int main(){
    int arr[10] ={ 3,16,1,4,2,63,45,9,43,6};
    size_t len = sizeof (arr)/sizeof (arr[0]);
    bubble_sort_new(arr,len,sizeof (arr[0]),compar_int);
    print_int(arr,len);
    return 0;
}